■ RS232C シリアル通信
USB、SPI、I2C通信などもシリアル通信ですが、パソコンの世界でシリアル通信と云えばCOMポートからのRS232C
準拠の通信を指すようです。このページのタイトルは単に”シリアル通信”でよいのかもしれません。
BCBにはシリアル通信用の標準コンポーネントは用意されていないので、シリアル通信を行う場合は以下の要領で
Win32APIを使って通信を行います。考え方としては コムポート経由の入出力送受信データをファイルのデータと
同様に扱い、通信固有の通信条件や設定に関しては専用の関数や構造体を用いるというものです。
(1) 全体的な流れ
@ CreateFile()関数で ファイルをオープンする。(COMポートのハンドルを取得する)
A SetupComm() PurgeComm()関数で送受信バッファーの設定及び初期化をおこなう
B DCB構造体、SetCommState()関数をもちいて初期設定、通信条件設定、ポート設定などをおこなう
C COMMTIMEOUTS構造体、SetCommTimeouts()関数をもちいてタイムアウトに関する通信設定をおこなう
D WriteFile()関数で データを送信する
E ReadFile()関数で データを受信する
F CloseHandle()関数でファイルを閉じる
(2) 具体的流れ
@ COMポートハンドルの取得
ポートをファイルとみなして CreateFile()関数でオープンしてハンドルを取得します。
HANDLE hPort; // (COM2ポートの)ポートハンドルを取得する hPort = CreateFile( "COM2", // ポートの名前: どのポートを開くのか GENERIC_READ|GENERIC_WRITE, // アクセスモード: 通常送受信ともするので読書き両方を指定 0, // 共有モード: 通常0に設定 再オープン禁止 NULL, //セキュリティアトリビュート:通信では通常NULLに設定 OPEN_EXISTING, // クリエイトディストリビューション:通常COMポートはすでに存在しているのでOPEN_EXISTINGとします。 FILE_ATTRIBUTE_NORMAL, // 属性:通信の場合属性はないのでFILE_ATTRIBUTE_NORMAL(属性なし)を指定 NULL // テンプレートのハンドル: 通信の場合関係ない 通常NULLを指定 ); if(hPort == INVALID_HANDLE_VALUE) //ハンドル取得に失敗した場合 { printf("Port could not open.\n"); exit(0); } |
A 送受信バッファ初期化
・ SetupComm()関数で送受信バッファーの容量を設定します。
・ PurgeComm()関数で送受信バッファーに残っているデータを破棄します。
(注)送受信バッファー容量ははドライバー、OSが介在するのでUARTそのもののハードウェア的サイズとは
ことなります。
HANDLE hPort; Ret = SetupComm( //設定 hPort, // 通信デバイスのハンドル:CreateFile()で取得したハンドルを指定 1024, // 受信バッファーサイズ: 受信のバッファーサイズをバイト単位で指定 1024 // 送信バッファーサイズ: 送信のバッファーサイズをバイト単位で指定 ); if(Ret == FALSE) // 失敗した場合 { printf("SetupComm failed.\n"); CloseHandle(hPort); exit(0); } Ret = PurgeComm( //消去 hPort, // 通信デバイスのハンドル:CreateFile()で取得したハンドルを指定 PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR // 実行する操作: 上記は未処理の読書きの中止及び送受信のバッファーのクリアを指定 ); if(Ret == FALSE) //失敗した場合 { printf("PurgeComm failed.\n"); CloseHandle(hPort); exit(0); } |
B 基本通信条件の設定 DCB( デバイス制御ブロック)構造体の変数を宣言して 各種通信条件を設定します。伝送速度、パリ ティ有無、ストップビット数、CTS/RTSハードウェアフロー制御及び、Xon/Xoffソフトウェアフロー制御の有無や 諸定数等の通信条件設定を行います。DCB構造体のメンバー設定後SetCommState()関数にポートハンドル およびDCB構造体のアドレスを代入します。 |
|
DCB dcb; // DCB構造体の変数を宣言 HANDLE hPort; dcb.DCBlength = sizeof(DCB); // DCBのサイズ: //データ dcb.BaudRate = 9600; // 伝送速度: ボーレートをbps単位で指定 dcb.fBinary = TRUE; // バイナリモード: 通常TRUEに設定 dcb.ByteSize = 8; // データサイズ : 通常 8 ビット dcb.fParity = NOPARITY; // パリティ有無種類 : パリティなしの場合はNOPARITYを指定 // 奇数パリティの場合は ODDPARITY 他 dcb.StopBits = ONESTOPBIT; // ストップビットの数: 通常1ビット→ ONESTOPBIT; //ハードウェアフロー制御 dcb.fOutxCtsFlow = FALSE; // CTSハードウェアフロー制御:CTS制御を使用しない場合はFLASEを指定 // CTS 制御をする場合はTRUEを指定してCTS信号を監視します。 dcb.fOutxDsrFlow = FALSE; // DSRハードウェアフロー制御:使用しない場合はFALSEを指定 dcb.fDtrControl = DTR_CONTROL_DISABLE; // DTR有効/無効: 無効なら DTR_CONTROL_DISABLE;ISABLE dcb.fRtsControl = RTS_CONTROL_DISABLE; // RTS制御: RTS制御をしない場合はRTS_CONTROL_DISABLEを指定 // RTS制御をする場合はRTS_CONTROL_ENABLE 等を指定 // ソフトウェアフロー制御 dcb.fOutX = FALSE; // 送信時XON/OFF制御の有無: なし→FLALSE dcb.fInX = FALSE; // 受信時XON/XOFF制御の有無:なし→FALSE dcb.fTXContinueOnXoff = TRUE; // 受信バッファー満杯&XOFF受信後の継続送信可否:送信可→TRUE dcb.XonLim = 512; // XONが送られるまでに格納できる最小バイト数: dcb.XoffLim = 512; // XOFFが送られるまでに格納できる最小バイト数: dcb.XonChar = 0x11; // 送信時XON文字 ( 送信可:ビジィ解除 ) の指定: // 一般に、XON文字として11H ( デバイス制御1:DC1 )がよく使われます dcb.XoffChar = 0x13; // XOFF文字(送信不可:ビジー通告)の指定:なし→FALSE // 一般に、XOFF文字として13H ( デバイス制御3:DC3 )がよく使われます //その他 dcb.fNull = TRUE; // NULLバイトの破棄: 破棄する→TRUE dcb.fAbortOnError = TRUE; // エラー時の読み書き操作終了: 終了する→TRUE dcb.fErrorChar = FALSE; // パリティエラー発生時のキャラクタ(ErrorChar)置換: なし→FLALSE dcb.ErrorChar = 0x00; // パリティエラー発生時の置換キャラクタ dcb.EofChar = 0x03; // データ終了通知キャラクタ : 一般に0x03(ETX)がよく使われます。 dcb.EvtChar = 0x02; // イベント通知キャラクタ : 一般に0x02(STX)がよく使われます Ret = SetCommState(hPort, &dcb); //SetCommState()関数にポートハンドルおよびdcb構造体のアドレスを代入する if(Ret == FALSE) // 失敗した場合 { printf("SetCommState failed.\n"); CloseHandle(hPort); exit(0); } |
C タイムアウト時間の設定 COMMTIMEOUT(コムポートタイムアウト)構造体の変数を宣言してタイムアウトに関する通信条件を設定 します。COMMTIMEOUT構造体のメンバー設定後SetCommTimeOut()関数にポートハンドルおよびDCB構造体 のアドレスを代入します。 |
|
COMMTIMEOUTS timeout; // COMMTIMEOUTS構造体の変数を宣言 HANDLE hPort; timeout.ReadIntervalTimeout = 500; // 文字読込時の2も時間の全体待ち時間(msec) timeout.ReadTotalTimeoutMultiplier = 0; //読込の1文字あたりの時間 timeout.ReadTotalTimeoutConstant = 500; //読込エラー検出用のタイムアウト時間 //(受信トータルタイムアウト) = ReadTotalTimeoutMultiplier * (受信予定バイト数) + ReadTotalTimeoutConstant timeout.WriteTotalTimeoutMultiplier = 0; //書き込み1文字あたりの待ち時間 timeout.WriteTotalTimeoutConstant = 500;//書き込みエラー検出用のタイムアウト時間 //(送信トータルタイムアウト) = WriteTotalTimeoutMultiplier * (送信予定バイト数) + WriteTotalTimeoutConstant Ret = SetCommTimeouts(hPort, &timeout);//SetCommTimeOut()関数にポートハンドルおよびCOMMTIMEOUTS構造体の //アドレスを代入します。 if(Ret == FALSE) //失敗した場合 { printf("SetCommTimeouts failed.\n"); CloseHandle(hPort); exit(0); } |
D 送信
ClearCommError()、COMSTAT構造体で送信可能か確認した後、 WriteFile()関数で データを送信します。
// 送信データ生成 BYTE bSendBuffer[1]; // 送信データ DWORD dwSendSize; // 送信データサイズ COMSTAT Comstat; // 通信デバイス情報構造体 DWORD dwErrorMask; // エラーコードを受け取る変数 HANDLE hPort; DataSize = strlen( str ); //データのサイズ取得 do { ClearCommError( // ポートの状態を取得してエラーをクリアする hPort , // 通信デバイスのハンドル:CreateFile()で取得したハンドルを指定 &dwErrorMask ,//下記のエラーコードのポインタを取得→エラーの判別 // CD_BREAK(フレーミングエラー)、 // CE_RXPARITY(パリティエラー)、 // CE_RXOVER(入力バッファーオーバーフロー)他 &Comstat//COMSTAT構造体へのポインタを取得 // Comstat.cbOutQue 送信バッファーの中に残っているデータ数 ); Application->ProcessMessages(); }while( ( (MAX - Comstat.cbOutQue) <= DataSize) || // 送信バッファが送信データを書き込める容量だけあるかチェック // MAX:送信バッファーサイズ ( Comstat.fCtsHold == FALSE ) // CTSがtrue(相手側が受信不可)なので送信不可 // (ハードウェアフロー制御をおこなう場合) ) // 相手側が受信可能でまた送信バッファーもデータにサイズに対して書き込める容量が空いている場合 // ループを抜ける ); Ret = WriteFile( //データの送信 hPort, // 通信デバイスのハンドル:CreateFile()で取得したハンドルを指定 bSendBuffer, // 送信データのポインタを指定 5, // 送信するデータのバイト数を指定 &dwSendSize, // 実際に送信されたバイト数(DWORD)が格納されるポインタを指定 NULL // 通信とは関係ない引数なのでNULLを指定 ); if(Ret == FALSE) //失敗した場合 { printf("WriteFile failed.\n"); CloseHandle(hPort); exit(0); } |
E 受信
EscapeCommFunction()関数で相手側に受信可能であることを示します(フロー制御がある場合)
ReadFile()関数で データを受信します。
char In[1]; DWORD lRead; int itemp; HANDLE hPort; // フロー制御関係の信号を送信(フロー制御をおこなう場合) Ret = EscapeCommFunction( hPort, // 通信デバイスのハンドル:CreateFile()で取得したハンドルを指定 SETRTS // 受信可能であることを相手側に示す:RTSをアクティブにする→SETRTS // (参考) RTSを非アクティブにする→CLRRTS ); if(Ret == FALSE) // 失敗した場合 { printf("EscapeCommFunction failed.\n"); CloseHandle(hPort); exit(0); } i Ret = ReadFile( // データの受信 hPort, // 通信デバイスのハンドル: CreateFile()で取得したハンドルを指定 In, // 受信バッファーのポインタを指定: 受信データがここに格納されます。 1, // 受信するバイト数を指定: ここで指定するバイト数を受信するかまたはタイムアウト時間がくるまで // ReadFile()関数は( getc()のように )待ちます &lRead, // 実際に受信したバイト数(DWORD)が格納されるポインタを指定 NULL // 通信とは関係ない引数なのでNULLを指定 ); if( Ret == FALSE ) //失敗した場合 { printf("ReadFile failed. \n" ) ; CloseHandle(hPort); exit(0); } |
F 終了処理
CloseHandle()関数でファイル(ポート)を閉じる
HANDLE hPort; // 終了処理 CloseHandle( hPort // 通信デバイスのハンドル: CreateFile()で取得したハンドルを指定 ); |
(3) 関数 と 構造体
シリアル通信関係の関数と構造体を一覧表にまとめてみました。 → 一覧表
(4) 実際のプログラム例
PCのCOMポートにPICを接続して文字列の送受信をおこなった例を紹介します
(4−1) 文字列の送受信(CTS/RTS ハードウェアフロー制御なし)
以下のWindowのコンボボックスから文字を送信して この結果により返信されてきた受信データを受信表示用の
エディットボックスに表示するプログラムです → プログラム例
(4−2) 文字列の送受信(CTS/RTS ハードウェアフロー制御あり)
PCから X = 0〜359(度)の整数を100msec毎に送信し PIC側に Y = sin ( 2× 3.1416 × X ÷ 360 )を
計算してもらう。この計算結果を返信してもらい以下のようなグラフに表示するプログラムです。 → プログラム例